﻿#region Using References

using System;
using System.Diagnostics.Contracts;
using Beaker.OpenCube.Game.Converters;
using Beaker.OpenCube.Mapping;

#endregion

// definition of level format as given by:
// http://www.minecraftwiki.net/wiki/Level.dat

namespace Beaker.OpenCube.Game
{
	/// <summary>
	/// 
	/// </summary>
	public sealed class LevelData
		: ObservableExpandoObject
	{
		[ContractInvariantMethod]
		private void ContractInvariant()
		{
			Contract.Invariant( Enum.IsDefined( typeof( GameMode ), _gameMode ) );

			_spawnCoordinates = Coordinates3<int>.Empty;
		}

		private int _version;

		/// <summary>
		/// Gets or sets the Version of the level (19133)
		/// </summary>
		[Mapping( Name = "version" )]
		public int Version
		{
			get
			{
				Contract.Ensures( Contract.Result<int>() == _version );
				return _version;
			}
			set
			{
				Contract.Ensures( _version == value );
				SetValue( ref _version, value );
			}
		}

		private bool _isInitialized;

		/// <summary>
		/// Gets or sets a value indicating whether this world has been initialized
		/// </summary>
		/// <value>
		/// <c>true</c> if this world is initialized; otherwise, <c>false</c>.
		/// </value>
		/// <remarks>
		/// Normally true after a world has been initialized properly after creation. 
		/// If the initial simulation was canceled somehow, this can be false 
		/// and the world will be re-initialized on next load.
		/// </remarks>
		[Mapping( Name = "initialized" )]
		public bool IsInitialized
		{
			get
			{
				Contract.Ensures( Contract.Result<bool>() == _isInitialized );
				return _isInitialized;
			}
			set
			{
				Contract.Ensures( _isInitialized == value );
				SetValue( ref _isInitialized, value );
			}
		}

		private string _levelName;

		/// <summary>
		/// Gets or sets the name of the level.
		/// </summary>
		/// <value>
		/// The name of the level.
		/// </value>
		public string LevelName
		{
			get
			{
				Contract.Ensures( Contract.Result<string>() == _levelName );
				return _levelName;
			}
			set
			{
				Contract.Ensures( _levelName == value );
				SetValue( ref _levelName, value );
			}
		}

		private string _generatorName;

		/// <summary>
		/// Gets or sets the name of the generator.
		/// </summary>
		/// <value>
		/// The name of the generator.
		/// </value>
		/// <remarks>
		/// The name of the generator; "default", "flat", or "largeBiomes". 
		/// Not case sensitive, but always written in the case here
		/// </remarks>
		[Mapping( Name = "generatorName" )]
		public string GeneratorName
		{
			get
			{
				Contract.Ensures( Contract.Result<string>() == _generatorName );
				return _generatorName;
			}
			set
			{
				Contract.Ensures( _generatorName == value );
				SetValue( ref _generatorName, value );
			}
		}

		private int _generatorVersion;

		/// <summary>
		/// Gets or sets the version of the level generator.
		/// </summary>
		/// <value>
		/// The version of the level generator.
		/// </value>
		/// <remarks>
		/// The effects of changing this are unknown, but values other than 0 have been observed.
		/// </remarks>
		[Mapping( Name = "generatorVersion" )]
		public int GeneratorVersion
		{
			get
			{
				Contract.Ensures( Contract.Result<int>() == _generatorVersion );
				return _generatorVersion;
			}
			set
			{
				Contract.Ensures( _generatorVersion == value );
				SetValue( ref _generatorVersion, value );
			}
		}

		private GeneratorOptions _generatorOptions;

		/// <summary>
		/// Gets or sets the generator options.
		/// </summary>
		/// <value>
		/// The generator options.
		/// </value>
		/// <remarks>Controls options for the generator, currently only the Superflat generator. The format is a comma separated list of block IDs from the bottom of the map up, and each block ID may optionally be preceded by the number of layers and an x. Damage values are not supported. </remarks>
		[Mapping( Name = "generatorOptions", Converter = typeof( GeneratorOptionsConverter ) )]
		public GeneratorOptions GeneratorOptions
		{
			get
			{
				Contract.Ensures( Equals( Contract.Result<GeneratorOptions>(), _generatorOptions ) );
				return _generatorOptions;
			}
			set
			{
				Contract.Ensures( Equals( _generatorOptions, value ) );
				SetValue( ref _generatorOptions, value );
			}
		}

		private long _randomSeed;

		/// <summary>
		/// Gets or sets the random level seed used to generate terrain.
		/// </summary>
		/// <value>
		/// The random level seed used to generate terrain.
		/// </value>
		public long RandomSeed
		{
			get
			{
				Contract.Ensures( Contract.Result<long>() == _randomSeed );
				return _randomSeed;
			}
			set
			{
				Contract.Ensures( _randomSeed == value );
				SetValue( ref _randomSeed, value );
			}
		}

		private bool _hasMapFeatures;

		/// <summary>
		/// Gets or sets a value indicating whether the map generator should place structures such as villages, strongholds and mineshafts.
		/// </summary>
		/// <value>
		/// <c>true</c> if the map generator should place structures such as villages, strongholds and mineshafts.; otherwise, <c>false</c>.
		/// </value>
		[Mapping( Name = "MapFeatures" )]
		public bool HasMapFeatures
		{
			get
			{
				Contract.Ensures( Contract.Result<bool>() == _hasMapFeatures );
				return _hasMapFeatures;
			}
			set
			{
				Contract.Ensures( _hasMapFeatures == value );
				SetValue( ref _hasMapFeatures, value );
			}
		}

		private DateTime _lastPlayed;

		/// <summary>
		/// Gets or sets the time the level was last loaded.
		/// </summary>
		/// <value>
		/// The time the level was last loaded.
		/// </value>
		[Mapping( Converter = typeof( UnixDateTimeConverter ) )]
		public DateTime LastPlayed
		{
			get
			{
				Contract.Ensures( Contract.Result<DateTime>() == _lastPlayed );
				Contract.Ensures( Contract.Result<DateTime>() >= UnixDateTimeConverter.UnixEpoch );
				return _lastPlayed;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>( value >= UnixDateTimeConverter.UnixEpoch );
				Contract.Ensures( _lastPlayed == value );
				SetValue( ref _lastPlayed, value );
			}
		}

		private bool _areCommandsAllowed;

		/// <summary>
		/// Gets or sets a value indicating whether 'cheat' commands are allowed.
		/// </summary>
		/// <value>
		///   <c>true</c> if cheat commands are allowed; otherwise, <c>false</c>.
		/// </value>
		[Mapping( Name = "allowCommands" )]
		public bool AreCommandsAllowed
		{
			get
			{
				Contract.Ensures( Contract.Result<bool>() == _areCommandsAllowed );
				return _areCommandsAllowed;
			}
			set
			{
				Contract.Ensures( _areCommandsAllowed == value );
				SetValue( ref _areCommandsAllowed, value );
			}
		}

		private bool _isHardcore;

		/// <summary>
		/// Gets or sets a value indicating whether this is a hardcore game.
		/// </summary>
		/// <value>
		/// <c>true</c> if this is a hardcore game; otherwise, <c>false</c>.
		/// </value>
		[Mapping( Name = "hardcore" )]
		public bool IsHardcore
		{
			get
			{
				Contract.Ensures( Contract.Result<bool>() == _isHardcore );
				return _isHardcore;
			}
			set
			{
				Contract.Ensures( _isHardcore == value );
				SetValue( ref _isHardcore, value );
			}
		}

		private GameMode _gameMode;

		/// <summary>
		/// Gets or sets the game mode.
		/// </summary>
		/// <value>
		/// The game mode.
		/// </value>
		[Mapping( Name = "GameType" )]
		public GameMode GameMode
		{
			get
			{
				Contract.Ensures( Enum.IsDefined( typeof( GameMode ), Contract.Result<GameMode>() ) );
				Contract.Ensures( Contract.Result<GameMode>() == _gameMode );
				return _gameMode;
			}
			set
			{
				Contract.Requires( Enum.IsDefined( typeof( GameMode ), value ) );
				Contract.Ensures( _gameMode == value );
				SetValue( ref _gameMode, value );
			}
		}

		private TimeSpan _gameTime;

		/// <summary>
		/// Gets or sets the game time.
		/// </summary>
		/// <value>
		/// The game time.
		/// </value>
		[Mapping( Name = "Time", Converter = typeof( GameTicksTimeSpanConverter ) )]
		public TimeSpan GameTime
		{
			get
			{
				Contract.Ensures( Contract.Result<TimeSpan>().Ticks >= 0 );
				Contract.Ensures( Contract.Result<TimeSpan>() == _gameTime );
				return _gameTime;
			}
			set
			{
				Contract.Requires( value.Ticks >= 0 );
				Contract.Ensures( _gameTime == value );
				_gameTime = value;
				SetValue( ref _gameTime, value );
			}
		}

		private TimeSpan _dayTime;

		/// <summary>
		/// Gets or sets the day time.
		/// </summary>
		/// <value>
		/// The day time.
		/// </value>
		[Mapping( Converter = typeof( GameTicksTimeSpanConverter ) )]
		public TimeSpan DayTime
		{
			get
			{
				Contract.Ensures( Contract.Result<TimeSpan>().Ticks >= 0 );
				Contract.Ensures( Contract.Result<TimeSpan>() == _dayTime );
				return _dayTime;
			}
			set
			{
				Contract.Requires( value.Ticks >= 0 );
				Contract.Ensures( _dayTime == value );
				SetValue( ref _dayTime, value );
			}
		}

		private Coordinates3<int> _spawnCoordinates;

		/// <summary>
		/// Gets or sets the spawn coordinates for this world.
		/// </summary>
		/// <value>
		/// The spawn coordinates for this world.
		/// </value>
		[Mapping( Names = new[] { "SpawnX", "SpawnY", "SpawnZ" }, Converter = typeof( Coordinates3Converter<int> ) )]
		public Coordinates3<int> SpawnCoordinates
		{
			get
			{
				Contract.Ensures( Contract.Result<Coordinates3<int>>() == _spawnCoordinates );
				return _spawnCoordinates;
			}
			set
			{
				Contract.Ensures( _spawnCoordinates == value );
				SetValue( ref _spawnCoordinates, value );
			}
		}

		private bool _isRaining;

		/// <summary>
		/// Gets or sets a value indicating whether it is rainging currently.
		/// </summary>
		/// <value>
		/// <c>true</c> if this it is rainging currently; otherwise, <c>false</c>.
		/// </value>
		[Mapping( Name = "raining" )]
		public bool IsRainging
		{
			get
			{
				Contract.Ensures( Contract.Result<bool>() == _isRaining );
				return _isRaining;
			}
			set
			{
				Contract.Ensures( _isRaining == value );
				SetValue( ref _isRaining, value );
			}
		}

		private TimeSpan _rainTime;

		/// <summary>
		/// Gets the time before <see cref="IsRainging"/> is toggled and this value gets set to another random value.
		/// </summary>
		/// <value>
		/// The time before <see cref="IsRainging"/> is toggled.
		/// </value>
		[Mapping( Name = "rainTime", Converter = typeof( GameTicksTimeSpanConverter ) )]
		public TimeSpan RainTime
		{
			get
			{
				Contract.Ensures( Contract.Result<TimeSpan>() == _rainTime );
				return _rainTime;
			}
			set
			{
				Contract.Ensures( _rainTime == value );
				SetValue( ref _rainTime, value );
			}
		}

		private bool _isThundering;

		/// <summary>
		/// Gets or sets if the rain/snow/cloud cover is a lightning storm and dark enough for mobs to spawn under the sky.
		/// </summary>
		/// <value>
		/// <c>true</c> if the rain/snow/cloud cover is a lightning storm; otherwise, <c>false</c>.
		/// </value>
		[Mapping( Name = "thundering" )]
		public bool IsThundering
		{
			get
			{
				Contract.Ensures( Contract.Result<bool>() == _isThundering );
				return _isThundering;
			}
			set
			{
				Contract.Ensures( _isThundering == value );
				SetValue( ref _isThundering, value );
			}
		}

		private TimeSpan _thunderTime;

		/// <summary>
		/// Gets the time before <see cref="IsThundering"/> is toggled and this value gets set to another random value.
		/// </summary>
		/// <value>
		/// The time before <see cref="IsThundering"/> is toggled.
		/// </value>
		[Mapping( Name = "thunderTime", Converter = typeof( GameTicksTimeSpanConverter ) )]
		public TimeSpan ThunderTime
		{
			get
			{
				Contract.Ensures( Contract.Result<TimeSpan>() == _thunderTime );
				return _thunderTime;
			}
			set
			{
				Contract.Ensures( _thunderTime == value );
				SetValue( ref _thunderTime, value );
			}
		}

		private PlayerData _playerData;

		/// <summary>
		/// Gets or sets the state of the Singleplayer player.
		/// </summary>
		/// <value>
		/// The state of the Singleplayer player.
		/// </value>
		[Mapping( IsOptional = true )]
		public PlayerData Player
		{
			get
			{
				Contract.Ensures( ReferenceEquals( Contract.Result<PlayerData>(), _playerData ) );
				return _playerData;
			}
			set
			{
				Contract.Ensures( ReferenceEquals( _playerData, value ) );
				SetValue( ref _playerData, value );
			}
		}

		private GameRules _gameRules;

		/// <summary>
		/// Gets or sets the game rules.
		/// </summary>
		/// <value>
		/// The game rules.
		/// </value>
		public GameRules GameRules
		{
			get
			{
				Contract.Ensures( ReferenceEquals( Contract.Result<GameRules>(), _gameRules ) );
				return _gameRules;
			}
			set
			{
				Contract.Ensures( ReferenceEquals( _gameRules, value ) );
				SetValue( ref _gameRules, value );
			}
		}
	}
}
